const loadjs_cache = {};
export function loadjs(url) {
	if (loadjs_cache[url]) return loadjs_cache[url];
	return (loadjs_cache[url] = new Promise(function (resolve, reject) {
		var script = document.createElement("script");
		script.type = "text/javascript";
		script.async = true;
		script.onload = resolve;
		script.onerror = function (err) {
			delete loadjs_cache[url];
			reject(err);
		};
		script.src = url;
		document.body.appendChild(script);
	}));
}

export function isWexin() {
	return ~navigator.userAgent.toLowerCase().indexOf("micromessenger");
}

export function isMobile() {
	return /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i.test(
		navigator.userAgent
	);
}

/**
 * @param {string} [ua]
 */
export function getOS(ua) {
	ua = ua || navigator.userAgent;
	const oss = {
		darwin: /os x/i,
		android: /Android/i,
		linux: /linux/i,
		iphone: /\(iPhone.*os (\d+)[._](\d+)/i,
		ipad: /\(iPad.*os (\d+)[._](\d+)/i,
		ios: /ios/i,
		chromeos: /cros/i,
		win32: /windows nt/i,
	};
	/** @type {keyof typeof oss} */
	let k;
	for (k in oss) {
		const v = oss[k];
		if (v.test(ua)) return k;
	}
	return "unknown";
}

/**
 * @param {HTMLElement} el
 * @param {string} cls
 */
export function animate(el, cls) {
	return new Promise(function (resolve) {
		el.classList.add(cls);
		el.classList.add(cls + "-active");
		setTimeout(function () {
			var styl = window.getComputedStyle(el);
			var delays = (styl.transitionDelay || styl.webkitTransitionDelay || "").split(", ");
			// @ts-ignore
			var durations = (styl.transitionDuration || styl.webkittransitionDuration || "").split(", ");
			var ms =
				delays.concat(durations).reduce(function (a, b) {
					return a + parseFloat(b) || 0;
				}, 0) * 1000;
			el.classList.replace(cls, cls + "-to");
			setTimeout(function () {
				el.classList.remove(cls + "-to");
				el.classList.remove(cls + "-active");
				resolve();
			}, ms);
		});
	});
}

/**
 * @param {string} dataurl
 */
export function dataURLtoBlob(dataurl) {
	var arr = dataurl.split(",");
	var mime = arr[0].match(/:(.*?);/)[1];
	var bstr = atob(arr[1]);
	var n = bstr.length;
	var u8arr = new Uint8Array(n);
	while (n--) u8arr[n] = bstr.charCodeAt(n);
	return new Blob([u8arr], {type: mime});
}

/**
 * 保存文件
 * @param {string|Blob|Uint8Array} txt
 * @param {string} [name]
 * @param {boolean} [newWindow]
 */
export function download(txt, name, newWindow) {
	if (/^(blob|https?):/.test(txt)) {
		let pms = Promise.reject();
		if (/^https?:/.test(txt) && window.fetch) {
			pms = fetch(txt)
				.then((res) => res.blob())
				.then((blob) => {
					return download(blob, name, newWindow);
				});
		}
		return pms.catch(() => {
			var a = document.createElement("a");
			a.href = txt;
			if (newWindow) {
				a.target = "_blank";
				a.rel = "noopener noreferrer";
			}
			a.download = name;
			a.click();
		});
	}
	if (txt == "[object Blob]") {
		let url = URL.createObjectURL(txt);
		setTimeout(function () {
			URL.revokeObjectURL(url);
		}, 1000);
		return download(url, name);
	}
	if (/^data:/.test(txt)) return download(dataURLtoBlob(txt), name);
	return download(new Blob([txt]), name);
}
/**
 * 复制字符串并返回是否复制成功
 * @param {string} str
 */
export function copy(str) {
	str = typeof str === "string" ? str : JSON.stringify(str);
	var success = false;
	try {
		var input = document.createElement("textarea");
		input.style.position = "fixed";
		input.style.top = "-100px";
		input.value = str;
		document.body.appendChild(input);
		input.select();
		input.setSelectionRange(0, str.length);
		success = document.execCommand("copy");
		document.body.removeChild(input);
	} catch (error) {}
	return success;
}

/**
 * 选中节点
 * @param {HTMLElement} el
 */
export function selectNode(el) {
	const selection = window.getSelection();
	selection.empty();
	const range = document.createRange();
	range.selectNode(el);
	selection.addRange(range);
}

/**
 * 获取文件
 * @template T extends boolean
 * @param {string} [accept] 'image/png'
 * @param {T} [multiple=false]
 * @returns {Promise<T extends true ? File[] : File>}
 */
export function pick(accept, multiple) {
	return new Promise(function (resolve, reject) {
		const input = document.createElement("input");
		input.type = "file";
		input.multiple = multiple;
		input.accept = accept == "image" ? "image/png|image/jpeg|image/gif" : accept;

		let count = 0;
		function hasSelectedFile() {
			if (input.value !== "") {
				resolve(multiple ? Array.from(input.files) : input.files[0]);
			} else if (++count >= 10) {
				reject("pick cancel");
			} else {
				setTimeout(hasSelectedFile, 50);
			}
		}

		function focus() {
			window.removeEventListener("focus", focus);
			hasSelectedFile();
		}

		window.addEventListener("focus", focus);
		input.click();
	});
}

/**
 * 获取文件夹
 * @returns {Promise<FileList>}
 */
export function pickDir() {
	return new Promise(function (resolve, reject) {
		var input = document.createElement("input");
		input.type = "file";
		input.webkitdirectory = true;
		input.onchange = function (e) {
			resolve(e.target.files);
		};
		input.click();
		input.onabort = reject;
		input.oncancel = reject;
	});
}
/**
 * 读取文件
 * @param {Blob} file
 * @param {"BinaryString"|"DataURL"|"base64"|"utf8"} [encoding]
 * @returns {Promise<string>}
 */
export function readFile(file, encoding) {
	return new Promise(function (resolve, reject) {
		var reader = new FileReader();
		if (encoding && reader["readAs" + encoding]) reader["readAs" + encoding](file);
		else reader.readAsText(file, encoding);
		reader.onload = function (e) {
			resolve(e.target.result);
		};
		reader.onerror = reject;
	});
}

/**
 * 读取文件
 * @param {Blob} file
 * @returns {Promise<ArrayBuffer>}
 */
export function readFileAsArrayBuffer(file) {
	return new Promise(function (resolve, reject) {
		var reader = new FileReader();
		reader.readAsArrayBuffer(file);
		reader.onload = function (e) {
			resolve(e.target.result);
		};
		reader.onerror = reject;
	});
}

/**
 * @returns {Promise<string>}
 */
export function pickImage() {
	return pick("image").then(function (file) {
		return readFile(file, "DataURL");
	});
}

/**
 *
 * @param {HTMLImageElement} image
 */
export function toCanvas(image) {
	var canvas = document.createElement("canvas");
	canvas.width = image.width;
	canvas.height = image.height;
	var ctx = canvas.getContext("2d");
	ctx.drawImage(image, 0, 0);
	return canvas;
}

/**
 * @param {HTMLCanvasElement} canvas
 * @param {string} [type]
 * @param {any} [quality]
 * @returns {Promise<Blob>}
 */
export function toBlob(canvas, type, quality) {
	return new Promise(function (resolve) {
		canvas.toBlob(resolve, type, quality);
	});
}

/**
 * 从URL|File中加载图片
 * @param {string|Blob} url
 * @returns {Promise<HTMLImageElement>}
 */
export function loadImage(url) {
	return new Promise(function (resolve, reject) {
		var img = document.createElement("img");
		if (url instanceof Blob) {
			img.src = URL.createObjectURL(url);
			img.onload = function () {
				resolve(img);
				URL.revokeObjectURL(url);
			};
			img.onerror = function (err) {
				reject(err);
				URL.revokeObjectURL(url);
			};
		} else {
			img.src = url;
			img.onload = resolve.bind(this, img);
			img.onerror = reject;
		}
	});
}

/**
 * 从URL中加载图片
 * @param {string} url
 * @returns {Promise<HTMLImageElement>}
 */
export function loadImageAsDataURL(url) {
	return new Promise(function (resolve, reject) {
		var img = document.createElement("img");
		img.src = url;
		img.onload = resolve.bind(this, img);
		img.onerror = reject;
	})
		.then(toCanvas)
		.then((canvas) => canvas.toDataURL("png"));
}

/**
 * @param {HTMLImageElement} img
 */
export function readImageData(img) {
	var canvas = document.createElement("canvas");
	canvas.width = img.naturalWidth;
	canvas.height = img.naturalHeight;
	var ctx = canvas.getContext("2d");
	ctx.drawImage(img, 0, 0);
	return ctx.getImageData(0, 0, canvas.width, canvas.height);
}

/**
 *
 * @param {HTMLElement} e
 * @param {HTMLElement} p
 */
export function isParent(e, p) {
	while (e && e.parentNode != p) e = e.parentNode;
	return e;
}

/**
 *
 * @param {CanvasImageSource} img
 * @param {Rect} rect
 * @param {string} [bgColor]
 */
export function crop(img, rect, bgColor) {
	var canvas = document.createElement("canvas");
	canvas.width = rect.width;
	canvas.height = rect.height;
	var ctx = canvas.getContext("2d");
	if (bgColor) {
		ctx.fillStyle = bgColor;
		ctx.fillRect(0, 0, canvas.width, canvas.height);
	}
	ctx.drawImage(img, -rect.x, -rect.y);
	return canvas;
}

export function loadFont(name, timeout) {
	timeout = timeout || 30e3;
	return new Promise(function (resolve, reject) {
		var span = document.createElement("span");
		span.innerText = "你好giItT1WQy@!-/#";
		span.style.fontSize = "300px";
		span.style.fontFamily = "sans-serif";
		span.style.position = "fixed";
		span.style.top = "-10000px";
		document.body.appendChild(span);
		var w = span.clientWidth;
		span.style.fontFamily = name;
		var b = +new Date();
		var h = setInterval(() => {
			if (+new Date() - b > timeout) {
				reject(new Error("timeout"));
				document.body.removeChild(span);
				clearTimeout(h);
			} else if (w != span.clientWidth) {
				resolve();
				document.body.removeChild(span);
				clearTimeout(h);
			}
		}, 100);
	});
}

// export function resize(img, max, fit, bgColor) {
// 	var canvas = document.createElement("canvas");
// 	canvas.width = max.width;
// 	canvas.height = max.height;
// 	var width = img.naturalWidth || img.width;
// 	var height = img.naturalHeight || img.height;
// 	var size = {width, height};
// 	contain(size, max);
// 	if (fit) {
// 		canvas.width = size.width;
// 		canvas.height = size.height;
// 	}
// 	var x = (canvas.width - size.width) / 2;
// 	var y = (canvas.height - size.height) / 2;
// 	var ctx = canvas.getContext("2d");
// 	if (bgColor) {
// 		ctx.fillStyle = bgColor;
// 		ctx.fillRect(0, 0, canvas.width, canvas.height);
// 	}
// 	ctx.drawImage(img, x, y, size.width, size.height);
// 	return canvas;
// }

export function flipX(img) {
	var canvas = document.createElement("canvas");
	var ctx = canvas.getContext("2d");
	canvas.width = img.naturalWidth || img.width;
	canvas.height = img.naturalHeight || img.height;
	ctx.transform(-1, 0, 0, 1, canvas.width, 0);
	ctx.drawImage(img, 0, 0);
	return canvas;
}

export function flipY(img) {
	var canvas = document.createElement("canvas");
	var ctx = canvas.getContext("2d");
	canvas.width = img.naturalWidth || img.width;
	canvas.height = img.naturalHeight || img.height;
	ctx.transform(1, 0, 0, -1, 0, canvas.height);
	ctx.drawImage(img, 0, 0);
	return canvas;
}

/**
 * @param {HTMLImageElement} img
 * @param {number} deg
 */
export function rotate(img, deg) {
	var canvas = document.createElement("canvas");
	var ctx = canvas.getContext("2d");
	var w = img.naturalWidth || img.width;
	var h = img.naturalHeight || img.height;
	var n = Math.sqrt(w * w + h * h);
	canvas.width = canvas.height = n;
	var x = (n - w) / 2;
	var y = (n - h) / 2;
	ctx.translate(n / 2, n / 2);
	ctx.rotate((deg / 180) * Math.PI);
	ctx.drawImage(img, x - n / 2, y - n / 2);
	var scale = img.width / img.naturalWidth;
	var rect = img.getBoundingClientRect();
	var rw = rect.width / scale;
	var rh = rect.height / scale;
	return crop(canvas, {
		x: (n - rw) / 2,
		y: (n - rh) / 2,
		width: (n + rw) / 2,
		height: (n + rh) / 2,
	});
}

export function addText(img, o) {
	var canvas = document.createElement("canvas");
	var ctx = canvas.getContext("2d");
	var w = (canvas.width = img.naturalWidth || img.width);
	var h = (canvas.height = img.naturalHeight || img.height);
	ctx.drawImage(img, 0, 0);
	ctx.font = ((16 / 240) * 400).toFixed(2) + "px Arial";
	ctx.strokeStyle = ctx.fillStyle = o.color || "#fff";
	var m = ctx.measureText(o.text);
	var x = (w - m.width) / 2;
	ctx.fillText(o.text, x, h - 12);
	return canvas;
}

export function measure(el) {
	var styl = window.getComputedStyle(el);
	var paddingLeft = parseFloat(styl.paddingLeft) || 0;
	var paddingRight = parseFloat(styl.paddingRight) || 0;
	var paddingTop = parseFloat(styl.paddingTop) || 0;
	var paddingBottom = parseFloat(styl.paddingBottom) || 0;
	var borderLeft = parseFloat(styl.borderLeft) || 0;
	var borderRight = parseFloat(styl.borderRight) || 0;
	var borderTop = parseFloat(styl.borderTop) || 0;
	var borderBottom = parseFloat(styl.borderBottom) || 0;
	var width = el.clientWidth - paddingLeft - paddingRight;
	var height = el.clientHeight - paddingTop - paddingBottom;
	return {
		paddingLeft,
		paddingRight,
		paddingTop,
		paddingBottom,
		borderLeft,
		borderRight,
		borderTop,
		borderBottom,
		width,
		height,
	};
}

/**
 * 返回一个Promise，在下次触发eventType时返回
 * @param {EventTarget} listener
 * @param {string} eventType
 */
export function once(listener, eventType) {
	var fn;
	return new Promise((resolve) => {
		listener.addEventListener(eventType, resolve);
		fn = function () {
			listener.removeEventListener(eventType, resolve);
		};
	}).finally(fn);
}

/**
 *
 * @param {DataTransfer} t
 * @return {{[path:string]:File}}
 */
export function getFilesFromDataTransfer(t) {}

let index = 1255;
/**
 * @param {HTMLElement} el
 */
export function pushIndex(el) {
	return (el.style.zIndex = index++);
}

export function detectZoom() {
	var ratio = 0;
	var screen = window.screen;
	var ua = navigator.userAgent.toLowerCase();

	if (window.devicePixelRatio !== undefined) {
		ratio = window.devicePixelRatio;
	} else if (~ua.indexOf("msie")) {
		if (screen.deviceXDPI && screen.logicalXDPI) {
			ratio = screen.deviceXDPI / screen.logicalXDPI;
		}
	} else if (window.outerWidth !== undefined && window.innerWidth !== undefined) {
		ratio = window.outerWidth / window.innerWidth;
	}

	return ratio;
}

export function waitAllImageLoaded() {
	let imgs = document.querySelectorAll("img");
	let list = [];
	for (let i = 0; i < imgs.length; i++) {
		let img = imgs[i];
		if (!img.complete) {
			list.push(
				new Promise(function (resolve) {
					img.addEventListener("load", resolve);
					img.addEventListener("error", resolve);
				})
			);
		}
	}
	return Promise.all(list);
}
